﻿using System.Collections.Generic;
using UnityEngine;

namespace MFramework
{
    public class GameObjectPool : Singleton<GameObjectPool>
    {
        private const float OBJECT_DEFAULT_LIFE_TIME = 120;

        private readonly Dictionary<string, GameObjectPoolCollection> m_poolDic = new Dictionary<string, GameObjectPoolCollection>();
        private readonly Dictionary<string, AssetHandle> m_handleDic = new Dictionary<string, AssetHandle>();

        private Transform m_rootTF;

        protected override void Init()
        {
            GameObject poolObject = new GameObject("[PoolObjects]");
            m_rootTF = poolObject.transform;

            Object.DontDestroyOnLoad(poolObject);

            MonoRuntime.Inst.Regist(MonoRuntime.MonoLifecycle.Update, Update);
        }

        private void Update()
        {
            foreach (GameObjectPoolCollection poolCollection in m_poolDic.Values)
            {
                poolCollection.ReleaseExpiredObjects();
            }
        }

        public void SetPoolObjectLifeTime(string key, float lifetime)
        {
            GameObjectPoolCollection poolCollection = GetPoolCollection(key);
            poolCollection.PoolObjectLiftTime = lifetime;
        }

        public GameObject Acquire(string key, string path, Transform parent = null)
        {
            GameObject instance = null;

            GameObjectPoolCollection poolCollection = GetPoolCollection(key);
            if(poolCollection.Count > 0)
            {
                instance = poolCollection.Dequeue();
            }

            if(instance == null)
            {
                AssetHandle handle = null;
                if (m_handleDic.ContainsKey(key))
                {
                    handle = m_handleDic[key];
                }
                if (handle == null || !handle.IsValid)
                {
                    handle = ResourcesManager.LoadAsset<GameObject>(path);
                    m_handleDic[key] = handle;
                }
                instance = handle.InstantiateSync();
            }

            //不可省略，需要把实例从DontDestroy场景中挪回来
            instance.transform.SetParent(parent);
            instance.SetActive(true);

            IPoolable poolable = instance.GetComponent<IPoolable>();

            if (poolable != null)
            {
                poolable.OnAcquire();
            }

            return instance;
        }

        public void Release(string key, GameObject instance)
        {
            GameObjectPoolCollection poolCollection = GetPoolCollection(key);

            poolCollection.Enqueue(instance);

            instance.transform.SetParent(m_rootTF);
            instance.SetActive(false);

            IPoolable poolable = instance.GetComponent<IPoolable>();

            if (poolable != null)
            {
                poolable.OnRelease();
            }
        }

        public void ClearPool(string key, bool releaseHandle = false)
        {
            GameObjectPoolCollection poolCollection = GetPoolCollection(key);

            poolCollection.PoolObjectLiftTime = -1;
            poolCollection.ReleaseExpiredObjects();

            m_poolDic.Remove(key);

            if (releaseHandle)
            {
                if (m_handleDic.ContainsKey(key))
                {
                    m_handleDic[key].Release();
                    m_handleDic.Remove(key);
                }
            }
        }

        public void ClearAllPool(bool releaseHandle = false)
        {
            foreach(GameObjectPoolCollection poolCollection in m_poolDic.Values)
            {
                poolCollection.PoolObjectLiftTime = -1;
                poolCollection.ReleaseExpiredObjects();
            }

            m_poolDic.Clear();

            if (releaseHandle)
            {
                foreach (AssetHandle handle in m_handleDic.Values)
                {
                    handle.Release();
                }
                m_handleDic.Clear();
            }
        }

        private GameObjectPoolCollection GetPoolCollection(string key)
        {
            if (!m_poolDic.ContainsKey(key))
            {
                GameObjectPoolCollection poolCollection = new GameObjectPoolCollection();
                poolCollection.PoolObjectLiftTime = OBJECT_DEFAULT_LIFE_TIME;

                m_poolDic.Add(key, poolCollection);
            }
            return m_poolDic[key];
        }
    }
}
